マルチサイクル RISC-V CPU を作成したい
https://gyazo.com/0d9c5448e09d85a03ad2ef2832e0a336
マルチサイクルな 32ビット RISC-V CPU の作成メモ。
シングルサイクルなCPUだと以下のような制限があるため、マルチサイクルなCPUを作ることにする。
プログラムメモリとデータメモリを共通化できない
ブロックRAM / SDRAM の読み込み待ちができない
浮動小数点数の計算待ちができない
リポジトリ
Odeeen
MinCamlコンパイラ
MinCamlのプログラムのビルドには、去年RISC-Vへ移植したこちらを利用する。
MinCamlのRISC-Vへの移植についてはこちらを参照
参考資料
シングルサイクルCPUのマルチサイクル化の手順については、以下が参考になりそう。
POCOのマルチサイクル化
メモリバス
今回のCPUはSDRAMに対応したい。SDRAMのデータの読み書きには複数クロックかかるので、SDRAMの読み書きが終わるまでCPU側が待機する必要がある。
以前作成したCPUのメモリバスは読み書きを待つための信号線を持たなかったので、今回のCPUでは、PicoRV32のメモリバスを参考にVALIDとREADYにより待ち合わせを行うインターフェースを採用する。 BRAMコントローラー
VALIDとREADYによる待ち合わせを行うメモリが必要なので、新たにBRAMコントローラーを作成した。
開発環境
開発記録
ステージ分割
1つのブロックRAMに「プログラムメモリ」と「データメモリ」の両方を格納したい。
「命令の取得」と「メモリへのデータの読み書き」を同時に行おうとすると、メモリバスで衝突が発生してしまうため、「命令を取得するステージ(IFステージ)」と「命令を実行するステージ(EXステージ)」とを分けることで、メモリバスの衝突を回避する。
IFステージ(命令フェッチ)
メインメモリ上の pc_reg が指す番地から命令を取得し、instr_reg へ格納
mem_addr に pc_reg の値をセット
pc_reg の更新はここでは行わない
EXステージ(実行)
instr_reg に格納した命令を実行
pc_reg の更新を行う
(追記)その後、データメモリへの読み書きの待ち合わせ用のステージが欲しくなったので、最終的に以下のようにステージ分けをおこなった。ステージの分け方については、デジタル回路設計とコンピュータアーキテクチャ(7章)が参考になった。
IFステージ(命令フェッチ)
EXステージ(実行)
MEMステージ(メモリアクセス)
WBステージ(レジスタ書き戻し、writeback)
https://gyazo.com/20646eff97e8edc11363d174ca80a49e
(さらに追記)その後、FPU導入にともない、以下のようになりました
code:verilog
typedef enum {
IF_STAGE,
EX_STAGE,
FP1_STAGE, // FPU へ入力を渡す
FP2_STAGE, // FPU からの出力を待機
FP3_STAGE, // FPU の出力をレジスタへ保存
MEM_STAGE,
WB_STAGE,
ERR_STAGE
} stage_t;
stage_t stage_reg, stage_next;
RISC-Vの命令セット
RISC-Vの命令セットについては以下が参考になる
実装予定の命令
当面は使う予定の無い以下の命令を除いた RV32I の命令を実装する
ecall
ebreak
lw 以外のロード系命令
sw 以外のストア系命令
最終的に実装した命令一覧はこちら
ビット列を浮動小数点数に変換するワンライナー
動作確認用のRubyのワンライナースクリプト。
code:ruby
指数部の最大値と最小値
0xFF
無限大 or NaN
0x00
ゼロ or 非正規数
最大
0xFE (指数 = 127)
仮数 * 170141183460469231731687303715884105728(2 ^ 127)
最小
0x01(指数 = -126)
仮数 * 1.1754943508222875e-38
Tang Nano 9K対応
発表資料